package ch6.part5;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

/**
 * 红包拆分算法
 * <p>
 * BigDecimal的进位和舍去
 * ROUND_UP          - 向上进位
 * ROUND_DOWN        - 向下舍入
 * ROUND_CEILING     - 向正无穷大进位
 * ROUND_FLOOR       - 向负无穷大舍入
 * ROUND_HALF_UP     - 四舍五入
 * ROUND_HALF_DOWN   - 0.5向下舍入
 * ROUND_HALF_EVEN   - 0.5向偶数舍入
 * ROUND_UNNECESSARY - 精确计算，需要进舍则报错
 *
 * @author liuwanxiang
 * @version 2019/06/18
 */
public class LuckyMoney {

    public static void main(String[] args) {
        List<BigDecimal> list;
        BigDecimal sum;
        list = divideV1(new BigDecimal(100), 10);
        sum = BigDecimal.ZERO;
        for (BigDecimal value : list) {
            System.out.println(value);
            sum = sum.add(value);
        }
        System.out.println(sum);

        System.out.println("------------------");

        list = divideV2(new BigDecimal(100), 10);
        sum = BigDecimal.ZERO;
        for (BigDecimal value : list) {
            System.out.println(value);
            sum = sum.add(value);
        }
        System.out.println(sum);

    }

    /**
     * 基于每次Random结果为[0.01, remainMoney/remainUser * 2 -1]的算法实现
     * Random.nextInt(n) 的取值范围为[0,n)，即为[1, n-1]中的整数
     *
     * @param totalMoney 红包总金额
     * @param totalUser  需要瓜分红包的用户总数
     * @return 红包拆分结果
     */
    private static List<BigDecimal> divideV1(BigDecimal totalMoney, int totalUser) {
        List<BigDecimal> result = new ArrayList<>();

        int remainMoney = totalMoney.multiply(new BigDecimal(100)).intValue();
        int remainUser = totalUser;
        Random random = new Random();

        for (int i = 1; i < totalUser; i++) {
            int localMoney = random.nextInt(remainMoney / (remainUser--) * 2 - 1) + 1;
            result.add(new BigDecimal(localMoney).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UNNECESSARY));
            remainMoney -= localMoney;
        }
        result.add(new BigDecimal(remainMoney).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UNNECESSARY));

        return result;
    }

    /**
     * 拆分红包V2
     * 标记线段上的每一个拆分点，这样的话，每一个点都是随机标注
     * 基于此，每个人的拆分金额都有可能极大或极小，可能会造成分配不均
     *
     * @param totalMoney 总金额（以分为单位）
     * @param totalUser  总人数
     */
    private static List<BigDecimal> divideV2(BigDecimal totalMoney, Integer totalUser) {

        int translateMoney = totalMoney.multiply(new BigDecimal(100)).intValue();

        List<BigDecimal> amountList = new ArrayList<>();
        Set<Integer> segments = new HashSet<>();
        Random random = new Random();
        for (int i = 0; i < totalUser - 1; i++) {
            int segment = random.nextInt(translateMoney - 2) + 1;
            int delta = random.nextInt(1) == 0 ? 1 : -1;
            while (segments.contains(segment) || segment == 0) {
                segment = (segment + delta) % translateMoney;
            }
            segments.add(segment);
        }

        List<Integer> segmentList = new ArrayList<>(segments);
        Collections.sort(segmentList);
        Integer amount;
        for (int i = 0; i < segmentList.size(); i++) {
            if (i == 0) {
                amount = segmentList.get(0);
            } else {
                amount = segmentList.get(i) - segmentList.get(i - 1);
            }
            amountList.add(new BigDecimal(amount).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UNNECESSARY));
        }
        amountList.add(new BigDecimal(translateMoney - segmentList.get(totalUser - 2)).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UNNECESSARY));

        return amountList;
    }

}